﻿using System.Diagnostics;
using System.Reflection;
using System.Security.Cryptography;
using Tetris.Game.Common.TetrisCenter.TetrisBase;
using Tetris.Game.ControlsBase;
using Tetris.Game.ControlsBase.Common;

namespace Tetris.Game.Common.TetrisCenter
{
    public unsafe class TetrisGameCenter : IDisposable
    {
        const int ROW = 20;
        const int COLUMN = 10;
        const int BLOCKSIZE = 36;
        private readonly Dictionary<ShapeType, Type> _blockShapeBaseTypes;
        private readonly Dictionary<ShapeType, Type> _tetrisShapeTypes;
        private readonly RNGCryptoServiceProvider _rng;
        private BlockShapeBase _lastBlockShapeBase;
        private GameStatus _gameStatus = GameStatus.Stop;
        private BlockControl[,] _gameBlockSet;
        private Queue<ShapeType> _nextSet;
        private TetrisGameResult _tetrisGameResult;
        private object _lockMove = new object();
        private byte[] buffer = new byte[4];
        const int MAXCOUNT = 3;
        private readonly ShapeType[] _shapeTypes = new ShapeType[MAXCOUNT];
        private int _shapeTypesIndex = 0;
        private readonly int[] _lastShapeTypes = new int[MAXCOUNT];
        private int[] _blockShapeBaseTypesArray;
        public ManualResetEvent _gameCneterEvent;
        public TetrisGameCenter()
        {
            Assembly currentAssembly = Assembly.GetEntryAssembly();
            Type attributeType = typeof(ShapeAttribute);
            Type blockShapeBaseType = typeof(BlockShapeBase);
            Type tetrisShapeBaseType = typeof(TetrisShapeBase);
            _blockShapeBaseTypes = RegisterType(currentAssembly, blockShapeBaseType, attributeType);
            _tetrisShapeTypes = RegisterType(currentAssembly, tetrisShapeBaseType, attributeType);
            _blockShapeBaseTypesArray = _blockShapeBaseTypes.Keys.Select(t => (int)t).ToArray();

            _rng = new RNGCryptoServiceProvider();
            _tetrisGameResult = new TetrisGameResult();
        }
        public Action<BlockControl[,]> InitData { get; set; }
        public Action<BlockControl[]> PaintBlockControlCallback { get; set; }
        public Action<BlockControl> UpdateLocationCallback { get; set; }
        public Action<TetrisShapeBase> NextTetrisShapeCallback { get; set; }
        public Action GameOverCallback { get; set; }
        public Action<BlockControl[], bool> ArrangeBlockCallback { get; set; }
        public Action<TetrisGameResult> GameResultCallback { get; set; }
        public Action CountDownStartCallback { get; set; }
        public GameStatus GameStatus
        {
            get { return _gameStatus; }
            set
            {
                if (value == _gameStatus) return;
                if (value == GameStatus.Start)
                {
                    StartGame();
                }
                else if (value == GameStatus.Stop)
                {
                    StopGame();
                }
                else
                {
                    SuspendGame();
                }
            }
        }
        private void Init()
        {
            GC.Collect();
            _lastBlockShapeBase.DisposeEx();
            _lastBlockShapeBase = null;
            InitData?.Invoke(_gameBlockSet);
            NextTetrisShapeCallback?.Invoke(null);

            _gameBlockSet = new BlockControl[ROW, COLUMN];
            _nextSet = new Queue<ShapeType>(2);

            // 初始的时候不让这两个形状出现---因为烦人，不喜欢
            _lastShapeTypes[0] = (int)ShapeType.S;
            _lastShapeTypes[1] = (int)ShapeType.Z;
            _lastShapeTypes[2] = (int)ShapeType.Z;
        }
        private void StartGame()
        {
            if (GameStatus == GameStatus.Start)
                return;
            if (GameStatus == GameStatus.Stop)
            {
                _gameStatus = GameStatus.Start;
                Init();
                Game();
            }
            else
            {
                _gameStatus = GameStatus.Start;
                Game();
            }
        }
        private void StopGame()
        {
            _gameStatus = GameStatus.Stop;
        }
        private void SuspendGame()
        {
            _gameStatus = GameStatus.Suspend;
        }
        /// <summary>
        /// 游戏启动
        /// </summary>
        private void Game()
        {
            Task.Run(() =>
            {
                while (GameStatus == GameStatus.Start)
                {
                    try
                    {
                        lock (_lockMove)
                        {
                            Stopwatch stopwatch = Stopwatch.StartNew();
                            BlockShapeBase blockShape = _lastBlockShapeBase;
                            if (blockShape == null || !blockShape.IsMove)
                            {
                                // 首次
                                if (blockShape == null)
                                {
                                    _gameCneterEvent = new ManualResetEvent(false);
                                    CountDownStartCallback.Invoke();
                                    _gameCneterEvent.WaitOne();
                                }

                                CreationBlockShape();
                                Thread.Sleep(400);
                            }
                            else
                            {
                                Move(blockShape, DirectionType.Down);
                            }
                            stopwatch.Stop();
                        }
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine(ex.Message + "；" + ex.StackTrace);
                    }
                    finally
                    {
                        Thread.Sleep(_tetrisGameResult.Interval);
                    }
                }
            });
        }
        public void CountDownEnd()
        {
            _gameCneterEvent.Set();
            _gameCneterEvent.Close();
            _gameCneterEvent = null;
        }
        public void Move(DirectionType direction)
        {
            if (GameStatus != GameStatus.Start || direction == DirectionType.None) return;
            lock (_lockMove)
            {
                Stopwatch stopwatch = Stopwatch.StartNew();
                BlockShapeBase? blockShape = _lastBlockShapeBase;
                if (blockShape == null || !blockShape.IsMove) return;

                if (direction == DirectionType.Up)
                {
                    // 旋转
                    RotateShape(blockShape);
                }
                else if (direction == DirectionType.Space)
                {
                    // 急速向下移动
                    while (Move(blockShape, DirectionType.Down)) ;
                }
                else
                {
                    // 移动
                    Move(blockShape, direction);
                }
                stopwatch.Stop();
            }
        }
        /// <summary>
        /// 移动
        /// </summary>
        /// <param name="blockShape"></param>
        /// <param name="direction"></param>
        /// <returns></returns>
        private bool Move(BlockShapeBase? blockShape, DirectionType direction)
        {
            var status= blockShape.Move(_gameBlockSet, direction);
         
            return status;
        }
        /// <summary>
        /// 更新坐标位置
        /// </summary>
        /// <param name="blockControl"></param>
        private void UpdateLocation(BlockControl blockControl)
        {
            if (UpdateLocationCallback != null)
            {
                UpdateLocationCallback(blockControl);
            }
        }
        /// <summary>
        /// 将四个小方块添加到二维数组中
        /// </summary>
        /// <param name="blockShape"></param>
        private void AddBlock(BlockShapeBase? blockShape)
        {
            if (PaintBlockControlCallback != null)
            {
                Task.Run(() =>
                {
                    PaintBlockControlCallback(blockShape.BlockControls);
                });
            }
            _lastBlockShapeBase = null;
            _lastBlockShapeBase = blockShape;
            fixed (BlockControl* block = blockShape.BlockControls, set = _gameBlockSet)
            {
                for (int i = 0; i < blockShape.BlockControls.Length; i++)
                {
                    BlockControl* cur = block + i;
                    *(set + block->PositionY * COLUMN + block->PositionX) = *block;
                }
            }
        }
        /// <summary>
        /// 创建下一个形状
        /// </summary>
        private void CreationNextShapeType()
        {
            ShapeType shapeType = GetRandomShapeType();
            _nextSet.Enqueue(shapeType);
            if (NextTetrisShapeCallback != null)
            {
                Task.Run(() =>
                {
                    TetrisShapeBase? tetrisShape = Activator.CreateInstance(_tetrisShapeTypes[shapeType]) as TetrisShapeBase;
                    NextTetrisShapeCallback(tetrisShape);
                });
            }
        }
        /// <summary>
        /// 创建当前形状
        /// </summary>
        private void CreationBlockShape()
        {
            ShapeType shapeType;
            if (_lastBlockShapeBase == null)
            {
                shapeType = GetRandomShapeType();
            }
            else
            {
                shapeType = _nextSet.Dequeue();
            }
            var blockShape = CreationBlockShape(shapeType);
            AddBlock(blockShape);
            CreationNextShapeType();
        }
        private BlockShapeBase? CreationBlockShape(ShapeType shapeType)
        {
            BlockShapeBase blockShape = Activator.CreateInstance(_blockShapeBaseTypes[shapeType]) as BlockShapeBase;
            blockShape.Size = BLOCKSIZE;
            blockShape.Row = ROW;
            blockShape.Column = COLUMN;
            blockShape.CreationBlock();
            blockShape.UpdateLocationCallback = UpdateLocation;
            blockShape.CheckEraseRowsCallback = CheckEraseRows;
            blockShape.GameOverCallback = GameOver;
            return blockShape;
        }
        private int GetRandom(int min, int max)
        {
            _rng.GetBytes(buffer);
            int randomNumber = BitConverter.ToInt32(buffer, 0);
            return Math.Abs(randomNumber % (max - min)) + min;// 左闭右开
        }
        private ShapeType GetRandomShapeType()
        {
            if (_shapeTypesIndex == 0)
            {
                fixed (int* ptr = _blockShapeBaseTypesArray)
                {
                    // 使用洗牌算法，打乱元素的位置
                    for (int i = _blockShapeBaseTypesArray.Length - 1; i > 0; i--)
                    {
                        int j = GetRandom(0, _blockShapeBaseTypesArray.Length);
                        int* cur = ptr + i;
                        int* random = ptr + j;
                        if (*cur != *random)
                        {
                            int temp = *cur;
                            *cur = *random;
                            *random = temp;
                        }
                    }
                }
                fixed (ShapeType* st = _shapeTypes)
                {
                    // 取差集，防止连续出现相同形状
                    RandomNums(_blockShapeBaseTypesArray.Except(_lastShapeTypes).ToArray(), st, MAXCOUNT);
                }
            }

            ShapeType shapeType = _shapeTypes[_shapeTypesIndex];
            _lastShapeTypes[_shapeTypesIndex] = ((int)shapeType);
            if (++_shapeTypesIndex == MAXCOUNT)
                _shapeTypesIndex = 0;
            return shapeType;
        }
        private void RandomNums(int[] arr, ShapeType* shapeType, int n)
        {
            fixed (int* arrPtr = arr)
            {
                // 采用洗牌算法，从差集中随机获取3个形状
                for (int i = 0; i < n; i++)
                {
                    int index = GetRandom(0, arr.Length - i);
                    *(shapeType + i) = (ShapeType)(*(arrPtr + index));
                    if (index != arr.Length - i - 1)
                        *(arrPtr + index) = *(arrPtr + arr.Length - i - 1);
                }
            }
        }
        private void GameOver()
        {
            GameStatus = GameStatus.Stop;
            GameOverCallback?.Invoke();
        }
        BlockControl[] _eliminateFullRowBlocks=new BlockControl[COLUMN];
        /// <summary>
        /// 检查是否满行
        /// </summary>
        private void CheckEraseRows()
        {
            bool status = false;
            bool isPlaySound = false;
            int rowNumber = 0;
            fixed (BlockControl* set = _gameBlockSet, fullRowBlocks= _eliminateFullRowBlocks)
            {
                for (int i = ROW - 1; i >= 0; i--)
                {
                    status = true;
                    for (int j = 0; j < COLUMN; j++)
                    {
                        BlockControl* block = set + i * COLUMN + j;
                        if (*block == null)
                        {
                            status = false;
                            break;
                        }
                    }
                    if (status)
                    {
                        ++rowNumber;
                        for (int j = 0; j < COLUMN; j++)
                        {
                            BlockControl* block = set + i * COLUMN + j;
                            *(fullRowBlocks + j) = *block;
                            * block = null;
                        }
                        ArrangeBlockCallback(_eliminateFullRowBlocks, true);
                        ArrangeBlock(i++);
                    }
                }
            }
            if (rowNumber > 0)
            {
                SoundType.Fight.PlaySound();
                // 添加得分
                _tetrisGameResult.AddRowNumber(rowNumber);
                Task.Run(() =>
                {
                    GameResultCallback(_tetrisGameResult);
                });
            }
        }

        /// <summary>
        /// 整理行
        /// </summary>
        /// <param name="curRow"></param>
        private void ArrangeBlock(int curRow)
        {
            int col = 0;
            List<BlockControl> blockControls = new List<BlockControl>(COLUMN);
            fixed (BlockControl* set = _gameBlockSet)
            {
                for (int i = curRow - 1; i >= 0; i--)
                {
                    if (blockControls.Count > 0)
                        blockControls.Clear();
                    col = 0;
                    for (int j = 0; j < COLUMN; j++)
                    {
                        BlockControl* block = set + i * COLUMN + j;
                        if (*block == null)
                        {
                            col++;
                            continue;
                        }
                        block->PositionY = i + 1;
                        *(block + COLUMN) = *block;
                        blockControls.Add(*block);
                        *block = null;
                    }
                    // 如果一行都没有方块出现，则认为上面也没有方块了，不需要继续遍历上面的方块
                    if (col == COLUMN) break;
                    if (blockControls.Count > 0 && ArrangeBlockCallback != null)
                    {
                        var arr = blockControls.ToArray();
                        Task.Run(() =>
                        {
                            ArrangeBlockCallback(arr, false);
                        });
                    }
                }
            }
            blockControls.Clear();
            blockControls = null;
        }

        /// <summary>
        /// 旋转
        /// </summary>
        /// <param name="blocks"></param>
        private void RotateShape(BlockShapeBase blockShape)
        {
            if (blockShape.RotateShape(_gameBlockSet))
            {
                if (ArrangeBlockCallback != null)
                {
                    Task.Run(() =>
                    {
                        ArrangeBlockCallback(blockShape.BlockControls, false);
                    });
                }
            }
        }

        private Dictionary<ShapeType, Type> RegisterType(Assembly assembly, Type baseType, Type attributeType)
        {
            return assembly.GetTypes().Where(t => t.BaseType == baseType && t.GetCustomAttributes(attributeType, false).Any())
                .Select(t =>
                {
                    ShapeAttribute? sa = t.GetCustomAttribute(attributeType, false) as ShapeAttribute;
                    if (!sa.Use)
                        return (ShapeType.L, attributeType);

                    return (sa.TetrisShapeType, t);
                }).Where(t => t.Item2 != attributeType).ToDictionary(t => t.Item1, t => t.Item2);
        }

        public void Dispose()
        {
            _rng.Dispose();
            Init();
            SoundFactory.Dispose();
        }
    }
}
